package com.zh.blog.common.websocket;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zh.blog.dao.ChatRecordDao;
import com.zh.blog.dto.ChatRecordDto;
import com.zh.blog.dto.RecallMessageDto;
import com.zh.blog.dto.WebsocketMessageDto;
import com.zh.blog.model.ChatRecord;
import com.zh.blog.strategy.context.UploadStrategyContext;
import com.zh.blog.util.HTMLUtils;
import com.zh.blog.util.IpUtils;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.websocket.*;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArraySet;

import static com.zh.blog.enums.ChatTypeEnum.*;

/**
 * websocket服务
 * @author ZhengHao
 * Created At 2022/8/17.
 **/
@Data
@Service
@ServerEndpoint(value = "/websocket",configurator = WebSocketServiceImpl.ChatConfigurator.class)
public class WebSocketServiceImpl {

    /**
     * 用户session
     */
    private Session session;

    /**
     * 用户session集合
     */
    private static CopyOnWriteArraySet<WebSocketServiceImpl>  webSocketSet = new CopyOnWriteArraySet<>();

    private static ChatRecordDao chatRecordDao;

    private static UploadStrategyContext uploadStrategyContext;

    @Autowired
    public void setChatRecordDao(ChatRecordDao chatRecordDao) {
        WebSocketServiceImpl.chatRecordDao = chatRecordDao;
    }

    @Autowired
    public void setUploadStrategyContext(UploadStrategyContext uploadStrategyContext) {
        WebSocketServiceImpl.uploadStrategyContext = uploadStrategyContext;
    }

    /**
     * 获取客户端真实IP
     */
    public static  class ChatConfigurator extends ServerEndpointConfig.Configurator {

        public static String HEADER_NAME = "X-Real-IP";

        @Override
        public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
            try {
                String firstFoundHeader = request.getHeaders().get(HEADER_NAME.toLowerCase()).get(0);
                sec.getUserProperties().put(HEADER_NAME,firstFoundHeader);
            } catch (Exception e) {
                sec.getUserProperties().put(HEADER_NAME,"未知ip");
            }
        }
    }

    /**
     * 连接建立成功调用的方法
     * @param session
     * @param endpointConfig
     * @throws IOException
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig endpointConfig) throws IOException {
        // 加入连接
        this.session = session;
        webSocketSet.add(this);
        // 更新在线人数
        updateOnlineCount();
        // 加载历史聊天记录
        ChatRecordDto chatRecordDto = listChartRecords(endpointConfig);
        // 发送消息
        WebsocketMessageDto messageDto = WebsocketMessageDto.builder()
                .type(HISTORY_RECORD.getType())
                .data(chatRecordDto)
                .build();
        synchronized (session) {
            session.getBasicRemote().sendText(JSON.toJSONString(messageDto));
        }
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 用户session
     * @throws IOException
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        WebsocketMessageDto messageDto = JSON.parseObject(message, WebsocketMessageDto.class);
        switch (Objects.requireNonNull(getChatType(messageDto.getType()))) {
            case SEND_MESSAGE:
                // 发送消息
                ChatRecord chatRecord = JSON.parseObject(JSON.toJSONString(messageDto.getData()), ChatRecord.class);
                // 过滤html标签
                chatRecord.setContent(HTMLUtils.filter(chatRecord.getContent()));
                chatRecordDao.insert(chatRecord);
                messageDto.setData(chatRecord);
                // 广播消息
                broadcastMessage(messageDto);
                break;
            case RECALL_MESSAGE:
                // 撤回消息
                RecallMessageDto recallMessageDto = JSON.parseObject(JSON.toJSONString(messageDto.getData()), RecallMessageDto.class);
                // 删除记录
                chatRecordDao.deleteById(recallMessageDto.getId());
                // 广播消息
                broadcastMessage(messageDto);
                break;
            case HEART_BEAT:
                // 心跳消息
                messageDto.setData("pong");
                session.getBasicRemote().sendText(JSON.toJSONString(JSON.toJSONString(messageDto)));
            default:
                break;
        }
    }

    @OnClose
    public void onClose() throws IOException {
        // 更新在线人数
        webSocketSet.remove(this);
        updateOnlineCount();

    }

    /**
     * 加载历史聊天记录
     * @param endpointConfig 配置
     * @return 历史聊天记录数据
     */
    private ChatRecordDto listChartRecords(EndpointConfig endpointConfig) {
        // 获取聊天历史记录
        List<ChatRecord> chatRecordList = chatRecordDao.selectList(new LambdaQueryWrapper<ChatRecord>()
                .ge(ChatRecord::getCreateTime, DateUtil.offsetHour(new Date(), -12)));
        // 获取当前用户IP
        String ipAddress = endpointConfig.getUserProperties().get(ChatConfigurator.HEADER_NAME).toString();
        return ChatRecordDto.builder()
                .chatRecordList(chatRecordList)
                .ipAddress(ipAddress)
                .ipSource(IpUtils.getIpSource(ipAddress))
                .build();
    }

    /**
     * 更新在线人数
     */
    @Async
    public void updateOnlineCount() throws IOException {
        // 获取当前在线人数
        WebsocketMessageDto messageDto = WebsocketMessageDto.builder()
                .type(ONLINE_COUNT.getType())
                .data(webSocketSet.size())
                .build();
        // 广播消息
        broadcastMessage(messageDto);
    }

    /**
     * 广播消息
     * @param messageDto 消息
     */
    private void broadcastMessage(WebsocketMessageDto messageDto) throws IOException{
        for (WebSocketServiceImpl webSocketService : webSocketSet) {
            synchronized (webSocketService.session) {
                webSocketService.session.getBasicRemote().sendText(JSON.toJSONString(messageDto));
            }
        }
    }
}